package server;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Array;
import java.nio.Buffer;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Properties;

import security.AsymmetricKeyEncryptor;
import security.Generator;
import security.SymmetricKeyEncryptor;


/**
 * 
 * @author MohamedNour
 */
public class ClientHandler{

	private String username;
	private Key symmetricKey;
	private DataOutputStream out;
	private DataInputStream in;
	private Server server;
	private int nonceSize = 64;
	private SecureRandom generator;

	public ClientHandler(OutputStream serverOut, InputStream serverIn, Server server) {
		this.out = new DataOutputStream(serverOut);
		this.in = new DataInputStream(serverIn);
		this.server = server;
		this.generator = new SecureRandom();
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public Key getSymmetricKey() {
		return symmetricKey;
	}

	public void setSymmetricKey(Key symmetricKey) {
		this.symmetricKey = symmetricKey;
	}

	public void startReceivingData() {
		this.run();
	}

	public void run() {
		while (true) {
			try {
				int messageLength = Integer.parseInt(in.readUTF());
				System.out.println("Before receiving message with " + messageLength);
				byte message[] = new byte[messageLength];				
				in.readFully(message);
				System.out.println("After receiving message with " + messageLength + " \n " + Arrays.toString(message));
				server.handleClientMessage(message, this);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public boolean authenticate() {
		int mode = server.getMode();
		byte[] decryptedNonce = null;
		byte nonce[] = null;
		try {
			out.writeUTF(""+mode);
			out.flush();
			nonce = new byte[nonceSize];
			generator.nextBytes(nonce);
			username = in.readUTF();
			System.out.println("Server accepted new client " + username);
			System.out.println("Server sends new nonce \n" + Arrays.toString(nonce) + "\n to " + username);
			sendData(nonce);
			int currentNonceSize = Integer.parseInt(in.readUTF());
			System.out.println("Waiting Nonce with size " + currentNonceSize);
			byte encryptedNonce[] = new byte[currentNonceSize];
			in.read(encryptedNonce);
			System.out.println("Server received encrypted nonce " + decryptedNonce + " from " + username);
			switch (mode) {
			case Server.PUBLIC_PRIVATE:
				decryptedNonce = decryptNonceWithAsym(encryptedNonce);
				break;
			case Server.PASSWORD:
				decryptedNonce = decryptNonceWithSym(encryptedNonce);
				break;
			}
			System.out.println("Server received decrypted nonce " + decryptedNonce + " from " + username);
		} catch (IOException e) {
			e.printStackTrace();
		}

		String nounce1 = new String(nonce);
		String nounce2 = new String(decryptedNonce);
		if (!nounce1.equals(nounce2)) {
			sendData("ERROR");
			return false;
		} else {
			sendData("OK");
			return true;
		}

	}

	private byte[] decryptNonceWithSym(byte[] encryptedNonce) {
//			DataInputStream in = new DataInputStream
//                                (new FileInputStream(new File("serverdata/" + username + "/password.prop")));
//                        int length = in.readInt();
//                        byte[] digest = new byte[length];
//                        in.readFully(digest);
//                        System.out.println(Arrays.toString(digest));
//			symmetricKey = Generator.getInstance().generateSymmetricKey(
//					digest);
		symmetricKey = SymmetricKeyEncryptor.getInstance().loadSymmetricKey(username, username+"key");
		System.out.println("Symmetric key = "
				+ Arrays.toString(symmetricKey.getEncoded()));
		return SymmetricKeyEncryptor.getInstance().decrypt(encryptedNonce, symmetricKey);
	}

	private byte[] decryptNonceWithAsym(byte[] encryptedNonce) {
		AsymmetricKeyEncryptor ake = AsymmetricKeyEncryptor.getInstance();
		PublicKey publicKey;
		try {
			publicKey = ake.loadPubliceKey("serverdata/" + username + "/");
			System.out.println("username = " + username);
			return ake.decrypt(encryptedNonce, publicKey);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (InvalidKeySpecException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	public synchronized void sendData(byte[] nonce) {
		try {
			out.write(nonce);
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public synchronized void sendData(String data) {
		try {
			out.writeUTF(data);
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public boolean equals(Object obj) {
		ClientHandler otherClient = (ClientHandler) obj;
		return (this.username.equals(otherClient.username));
	}
	
	@Override
	public String toString() {
		return username;
	}

}